/*
 * Niiuzu
 * Copyright (C) 2011  Thierry Nowak
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.niiuzu.server.dao.impl;

import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.Query;
import com.niiuzu.server.dao.UserDao;
import com.niiuzu.server.dao.exception.DaoException;
import com.niiuzu.server.dao.impl.dto.object.DtoUser;
import com.niiuzu.server.dao.impl.dto.object.DtoUserCredentials;
import com.niiuzu.server.dao.impl.util.KeyUtil;
import com.niiuzu.server.util.PasswordUtil;
import com.niiuzu.shared.model.User;

/**
 * Implémentation du DAO dédié aux utilisateurs stockés dans le magasin de
 * données.
 */
public class UserDaoImpl extends AbstractDatastoreDao implements UserDao {

	private static final Logger LOGGER = Logger.getLogger(UserDaoImpl.class
			.getName());

	static {
		ObjectifyService.register(DtoUser.class);
		ObjectifyService.register(DtoUserCredentials.class);
	}

	@Override
	public User getUserWithCredentials(String mail, String password)
			throws DaoException {

		LOGGER.info("Récupération de l'utilisateur " + mail);

		User user = null;

		try {

			Objectify objectify = createObjectify(false);

			Query<DtoUser> userQuery = objectify.query(DtoUser.class);
			DtoUser dtoUser = userQuery.filter("mail", mail).get();

			// Si un utilisateur a été trouvé
			if (dtoUser != null) {
				KeyUtil keyUtil = getKeyUtil();

				Query<DtoUserCredentials> credentialsQuery;
				credentialsQuery = objectify.query(DtoUserCredentials.class);

				DtoUserCredentials dtoUserCredentials = credentialsQuery
						.ancestor(dtoUser).get();

				// Si des identifiants sont liés à l'utilisateur
				if (dtoUserCredentials != null) {

					// Génération de l'empreinte du mot de passe envoyé
					String salt = dtoUserCredentials.getHashedPasswordSalt();
					String hashedPassword = dtoUserCredentials
							.getHashedPassword();
					String otherHashedPassword = PasswordUtil.encodePassword(
							password, salt);

					// Comparaison des deux empreintes
					if (otherHashedPassword.equals(hashedPassword)) {
						user = new User();
						user.setUid(keyUtil.getEncodedKeyFor(dtoUser));
						user.setMail(dtoUser.getMail());
					}
				}
			}

		} catch (Exception e) {
			String message = "Erreur lors de la récupération de l'utilisateur "
					+ mail;
			LOGGER.log(Level.SEVERE, message, e);
			throw new DaoException(message, e);
		} finally {
			LOGGER.info("Fin récupération de l'utilisateur " + mail);
		}

		return user;
	}

	@Override
	public User getUserByEmail(String userMail) throws DaoException {

		LOGGER.info("Récupère l'utilisateur depuis l'email " + userMail);

		User result = null;

		try {

			Objectify objectify = createObjectify(false);
			KeyUtil keyUtil = getKeyUtil();

			Query<DtoUser> userQuery = objectify.query(DtoUser.class);
			DtoUser dtoUser = userQuery.filter("mail", userMail).get();

			if (dtoUser == null) {
				return null;
			}

			String uid = keyUtil.getEncodedKeyFor(dtoUser);

			result = new User();
			result.setUid(uid);
			result.setMail(dtoUser.getMail());

		} catch (Exception e) {
			String message = "Erreur lors de la récupération de l'utilisateur depuis l'email "
					+ userMail;
			LOGGER.log(Level.SEVERE, message, e);
			throw new DaoException(message, e);
		} finally {
			LOGGER
					.info("Fin du test de la récupération de l'utilisateur depuis l'email "
							+ userMail);
		}

		return result;
	}

	@Override
	public User createUserAccount(String mail, String password)
			throws DaoException {

		LOGGER.info("Début de création d'un compte pour " + mail);

		Objectify objectify = null;

		User user = null;

		try {

			objectify = createObjectify(true);
			KeyUtil keyUtil = getKeyUtil();

			// Hachage du mot de passe
			String salt = PasswordUtil.generateSalt();
			String hashedPassword = PasswordUtil.encodePassword(password, salt);

			DtoUser dtoUser = new DtoUser();
			dtoUser.setMail(mail);
			dtoUser.setCreationDate(new Date());

			objectify.put(dtoUser);

			Key<DtoUser> dtoUserKey = keyUtil.getKeyFor(dtoUser);
			DtoUserCredentials dtoUserCredentials = new DtoUserCredentials();
			dtoUserCredentials.setUserKey(dtoUserKey);
			dtoUserCredentials.setHashedPassword(hashedPassword);
			dtoUserCredentials.setHashedPasswordSalt(salt);

			objectify.put(dtoUserCredentials);

			String userUid = keyUtil.getEncodedKeyFor(dtoUser);
			user = new User();
			user.setMail(mail);
			user.setUid(userUid);

			commit(objectify);

		} catch (Exception e) {
			rollback(objectify);

			String message = "Erreur lors de la création d'un compte pour  "
					+ mail;
			LOGGER.log(Level.SEVERE, message, e);
			throw new DaoException(message, e);
		} finally {
			LOGGER.info("Fin de création d'un compte pour  " + mail);
		}

		return user;
	}

	@Override
	public boolean isEmailAddressAlreadyUsed(String mail) throws DaoException {

		LOGGER.info("Teste l'unicité de l'adresse mail " + mail);

		boolean result = false;

		try {

			Objectify objectify = createObjectify(false);

			Query<DtoUser> userQuery = objectify.query(DtoUser.class);
			DtoUser dtoUser = userQuery.filter("mail", mail).get();

			result = (dtoUser != null);

		} catch (Exception e) {
			String message = "Erreur lors du test de l'unicité de l'adresse mail "
					+ mail;
			LOGGER.log(Level.SEVERE, message, e);
			throw new DaoException(message, e);
		} finally {
			LOGGER.info("Fin du test de l'unicité de l'adresse mail " + mail);
		}

		return result;
	}

	@Override
	public void updateUserPassword(User user, String password)
			throws DaoException {

		LOGGER.info("Début de la mise à jour du mot de passe pour "
				+ user.getUid());

		Objectify inputObjectify = null;

		try {

			KeyUtil keyUtil = getKeyUtil();

			Key<DtoUser> dtoUserKey = keyUtil.convertStringToKey(user.getUid(),
					DtoUser.class);

			Objectify outputObjectify = createObjectify(false);
			Query<DtoUserCredentials> query;
			query = outputObjectify.query(DtoUserCredentials.class);
			DtoUserCredentials dtoUserCredentials;
			dtoUserCredentials = query.ancestor(dtoUserKey).get();

			final String salt = PasswordUtil.generateSalt();
			final String hash = PasswordUtil.encodePassword(password, salt);

			dtoUserCredentials.setHashedPassword(hash);
			dtoUserCredentials.setHashedPasswordSalt(salt);

			inputObjectify = createObjectify(true);
			inputObjectify.put(dtoUserCredentials);

			commit(inputObjectify);

		} catch (Exception e) {
			rollback(inputObjectify);

			String message = "Erreur lors de la mise à jour du mot de passe pour "
					+ user.getUid();
			LOGGER.log(Level.SEVERE, message, e);
			throw new DaoException(message, e);
		} finally {
			LOGGER.info("Fin de la mise à jour du mot de passe pour "
					+ user.getUid());
		}
	}
}
